var map = null;

var feeds = ['usgs25','usgs5','emsc','gfz'];
var urls = { 'usgs25' : '/ajxp/usgseq-feed.xml',
             'usgs5'  : '/ajxp/usgseq5-feed.xml',
             'emsc'   : '/ajxp/emsceq-feed.xml',
             'gfz'    : '/ajxp/gfzeq-feed.xml'
};

var currentfeed = '';
var blockswitch = false;
var timer = null;
var lastupdate = null;
var updateinterval = 300000; /* 5 minutes */

var timeline = null;
var eventSource = null;
var resizeTimerID = null;

var events = [];

var isIE = (navigator.userAgent.indexOf('MSIE') > -1);

function initMashup() {
    // IE needs, of course, special treatment
    if (isIE) {
        document.getElementById('timeline').style.height = document.documentElement.clientHeight / 4;
    }

    // initialise Google Maps
    if (GBrowserIsCompatible()) {
        map = new GMap2(document.getElementById("map"));
        map.addControl(new GLargeMapControl());
        map.addControl(new GMapTypeControl());
        map.addControl(new feedSelector());
        map.addMapType(G_PHYSICAL_MAP);
        map.removeMapType(G_HYBRID_MAP);
        map.enableDoubleClickZoom();
        map.enableScrollWheelZoom();
        map.enableContinuousZoom();
        map.setCenter(new GLatLng(0, 0), 4);
        map.setMapType(G_PHYSICAL_MAP);
    }

    // initialise Simile Timeline with two bands
    eventSource = new Timeline.DefaultEventSource();
    var bandInfos = [
                     Timeline.createBandInfo({
                                   eventSource:    eventSource,
                                   width:          "80%", 
                                   intervalUnit:   Timeline.DateTime.HOUR, 
                                   intervalPixels: 100
                                                       }),
                     Timeline.createBandInfo({
                                   showEventText:  false,
                                   eventSource:    eventSource,
                                   width:          "20%", 
                                   intervalUnit:   Timeline.DateTime.DAY, 
                                   intervalPixels: 100,
                                   trackHeight:    0.75,
                                   trackGap:       0.2
                                                       })
    ];
    // synchronise bands
    bandInfos[1].syncWith = 0;
    bandInfos[1].highlight = true;
    timeline = Timeline.create(document.getElementById("timeline"), bandInfos);
    // hijack popup window callback to show earthquake
    Timeline.DurationEventPainter.prototype._showBubble = function(x, y, evt) {
        GEvent.trigger(evt.marker, 'click');
        map.panTo(evt.marker.getPoint());
    }
    // add callback for window resize
    // here instead of the html file, to conform with XHTML 1.0
    window.onresize = onResize;

    // initialise feedselector, if possible with saved state
    var cookie = document.cookie;
    var start = cookie.indexOf('mapstate=');
    if (start == -1) {
        switchFeed('usgs25');
    } else {
        var end = cookie.indexOf(';', start);
        if (end == -1) {
            var savedfeed = decodeURIComponent(cookie.substring(start+9));
        } else {
            var savedfeed = decodeURIComponent(cookie.substring(start+9, end));
        }
        switchFeed(savedfeed);
    }

    // start the counter, tick every second
    setInterval('showCounter()', 1000);
}

// add additional buttons to Google Maps, see GM API docs for more information
function feedSelector() {}
feedSelector.prototype = new GControl;

feedSelector.prototype.initialize = function(map) {
    var container = document.createElement('div');
    var buttons = document.getElementById('buttons');

    for (var i = 0; i < feeds.length; i++) {
        makeButton(feeds[i],buttons,container);
    }

    var counter = buttons.removeChild(document.getElementById('counter'));
    container.appendChild(counter);
    var follow = buttons.removeChild(document.getElementById('follow'));
    container.appendChild(follow);
    var info = buttons.removeChild(document.getElementById('info'));
    container.appendChild(info);

    map.getContainer().appendChild(container);
    return(container);
}

function makeButton (feed, buttons, container) {
    var button = buttons.removeChild(document.getElementById(feed));
    container.appendChild(button);
    GEvent.addDomListener(button, 'click', function() {
        switchFeed(feed);
    });
}

feedSelector.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(7,17));
}

function showCounter() {
    // show remaining time until next reload of RSS feed
    var now = new Date();
    var elapsed = updateinterval - (now - lastupdate);
    var min = Math.floor(elapsed / 60000);
    var sec = Math.floor((elapsed - 60000*min) / 1000);
    if (sec < 10) { sec = '0'+sec; }
    document.getElementById('counter').innerHTML = min+':'+sec;
}

function switchFeed (newfeed) {
    // switch to different feed
    if ((newfeed != currentfeed) && !blockswitch) {
        // change style of button(s)
        if (currentfeed != '') {
            document.getElementById(currentfeed).setAttribute('class','button');
        }
        document.getElementById(newfeed).setAttribute('class','buttonselected');
        // stop the timer
        if (timer) {
            clearInterval(timer);
        }
        // switch to new feed
        currentfeed = newfeed;
        loadFeed();
        // restart the timer
        timer = setInterval('loadFeed()', updateinterval);
        // update cookie with new feed
        document.cookie = 'mapstate='+encodeURIComponent(currentfeed)+'; max-age='+(60*60*24*7);
    }
}

function loadFeed() {
    // clean map and timeline and reload everything
    map.clearOverlays();
    events = [];
    eventSource.clear();
    // reset the timer
    lastupdate = new Date();
    // block switching to different feed, until this one is loaded
    blockswitch = true;
    // load the currently selected feed
    GDownloadUrl(urls[currentfeed], showFeed);
}

function showFeed(data, responseCode) {
    if (responseCode == 200) {
        // allow switching to other feed again
        blockswitch = false;
        // center on most recent event?
        var followYN = document.getElementById('checkfollow').checked;
        var xml = GXml.parse(data);
        // iterate over all items and call the appropriate parser
        var items = xml.documentElement.getElementsByTagName('item');
        for (var i = items.length - 1; i >= 0; i--) {
            var eq = null;
            switch(currentfeed) {
            case 'usgs25':
                eq = parseUSGS(items[i]);
                break;
            case 'usgs5':
                eq = parseUSGS(items[i]);
                break;
            case 'emsc':
                eq = parseEMSC(items[i]);
                break;
            case 'gfz':
                eq = parseGFZ(items[i]);
                break;
            }
            // if data was valid, display event on map
            if (eq.mag) {
                map.addOverlay(createMarker(eq.pos,eq.date,eq.mag,eq.loc,eq.depth,eq.link));
            }
            // if desired, center on most recent earthquake
            if (i == 0 && followYN) {
                map.panTo(eq.pos);
            }
        }
        // add all events to the timeline 
        eventSource.addMany(events);
        // and, if desired,  move the timeline to the most recent entry
        if (followYN) {
            timeline.getBand(0).setCenterVisibleDate(eventSource.getLatestDate());
        }
    } else {
        alert("Unable to load data feed: "+responseCode);
    }
}

function parseUSGS (item) {
    // parse the USGS feeds
    var eq = {};
    var datestring = item.getElementsByTagName('pubDate')[0].firstChild.data;
    eq.date = new Date(Date.parse(datestring));
    var titlepat = /^M\s([\d\.]*), (\w.*)/;
    var titlestring = item.getElementsByTagName('title')[0].firstChild.data;
    var titlearray = titlepat.exec(titlestring);
    if (titlearray) {
        eq.mag = titlearray[1];
        eq.loc = titlearray[2];
    }
    eq.link = item.getElementsByTagName('link')[0].firstChild.data;
    try {
        var lat = item.getElementsByTagName('lat')[0].firstChild.data;
        var lon = item.getElementsByTagName('long')[0].firstChild.data;
    } catch (e) {
        var lat = item.getElementsByTagName('geo:lat')[0].firstChild.data;
        var lon = item.getElementsByTagName('geo:long')[0].firstChild.data;
    }
    eq.pos = new GLatLng(parseFloat(lat), parseFloat(lon));
    try {
        var depth = item.getElementsByTagName('subject')[2].firstChild.data;
    } catch (e) {
        var depth = item.getElementsByTagName('dc:subject')[2].firstChild.data;
    }
    eq.depth = depth;
    return(eq);
}

function parseEMSC (item) {
    // parse the EMSC feed
    // sometimes, essential data like the magnitude is missing
    var eq = {};
    var datestring = item.getElementsByTagName('pubDate')[0].firstChild.data;
    eq.date = new Date(Date.parse(datestring));
    var titlepat = /^\w+\s+([\d\.]*)\s+(\w.*)/;
    var titlestring = item.getElementsByTagName('title')[0].firstChild.data;
    var titlearray = titlepat.exec(titlestring);
    if (titlearray) {
        eq.mag = titlearray[1];
        eq.loc = titlearray[2];
    }
    eq.link = item.getElementsByTagName('link')[0].firstChild.data;
    try {
        var lat = item.getElementsByTagName('lat')[0].firstChild.data;
        var lon = item.getElementsByTagName('long')[0].firstChild.data;
    } catch (e) {
        var lat = item.getElementsByTagName('geo:lat')[0].firstChild.data;
        var lon = item.getElementsByTagName('geo:long')[0].firstChild.data;
    }
    eq.date = new Date(Date.parse(datestring));
    eq.pos = new GLatLng(parseFloat(lat), parseFloat(lon));
    try {
        var depth = item.getElementsByTagName('depth')[0].firstChild.data;
    } catch (e) {
        var depth = item.getElementsByTagName('emsc:depth')[0].firstChild.data;
    }
    eq.depth = depth.replace(/ *$/,'') + ' km';
    return(eq);
}

function parseGFZ (item) {
    // parse the GFZ feed
    // most of the data is crammed into the description string
    var eq = {};
    var dateposdeppat = /^(\d+)-(\d+)-(\d+)\s+(\d+):(\d+):(\d+)\s+([\d\.-]*)\s+([\d\.-]*)\s+(\d+) km.*/;
    var descstring = item.getElementsByTagName('description')[0].firstChild.data;
    var dateposdeparray = dateposdeppat.exec(descstring);
    if (dateposdeparray) {
        var date = new Date;
        date.setUTCFullYear(dateposdeparray[1]);
        date.setUTCMonth(dateposdeparray[2]-1);
        date.setUTCDate(dateposdeparray[3]);
        date.setUTCHours(dateposdeparray[4]);
        date.setUTCMinutes(dateposdeparray[5]);
        date.setUTCSeconds(dateposdeparray[6]);
        eq.date = date;
        var lat = dateposdeparray[7];
        var lon = dateposdeparray[8];
        eq.pos = new GLatLng(parseFloat(lat), parseFloat(lon));
	eq.depth = dateposdeparray[9] + ' km';
    }
    var titlepat = /^M\s+([\d\.]*),\s+(\w.*)/;
    var titlestring = item.getElementsByTagName('title')[0].firstChild.data;
    var titlearray = titlepat.exec(titlestring);
    if (titlearray) {
        eq.mag = titlearray[1];
        eq.loc = titlearray[2];
    }
    eq.link = item.getElementsByTagName('link')[0].firstChild.data;
    return(eq);
}

function createMarker (point, date, magstring, location, depth, link) {
    // create a marker on the map
    // age in days
    var age = (lastupdate.getTime() - date.getTime()) / 86400000;
    var grey = '0';
    if (age > 0.2) { grey = '4'; }
    if (age > 1) { grey = '8'; }
    if (age > 4) { grey = 'c'; }
    // create icon, size proportional to magnitude
    // use exponential icon size for USGS 5+ feed
    var mag = parseFloat(magstring);
    if (currentfeed == 'usgs5') {
        var icondir = 'icons_exp';
        var iconcenter = Math.sqrt(Math.pow(10,mag-3))/3;
    } else {
        var icondir = 'icons';
        var iconcenter = mag * 3;
    }
    var icon = new GIcon();
    if (isIE) {
        icon.image = icondir+'/eq'+magstring+'.gif';
    } else {
        icon.image = icondir+'/eq'+magstring+'_'+grey+'.png';
    }
    icon.iconAnchor = new GPoint(iconcenter , iconcenter);
    icon.infoWindowAnchor = new GPoint(iconcenter, iconcenter);
    var marker = new GMarker(point, {icon:icon, zIndexProcess:zIndexByAge});
    marker.age = age;
    // clicking the icon opens a window with some data and a link to
    // the feed provider's information page
    var html ='<div class="title">'+location+'</div>'+
              '<div>Magnitude: '+magstring+'</div>'+
              '<div>Depth: '+depth+'</div>'+
              '<div>Time: '+date.toGMTString()+'</div>'+
              '<div><a href="'+link+'">more information</a></div>';
    GEvent.addListener(marker, "click", function() {
        marker.openInfoWindow(html);
        timeline.getBand(0).scrollToCenter(date);
    });
    // create a color coded marker on the timeline
    var color = 'green';
    if (mag >= 4) { color = 'yellow'; }
    if (mag >= 6) { color = 'red'; }
    var evt = new Timeline.DefaultEventSource.Event(date,
                                                    null,null,null,false,
                                                    magstring+' '+location,
                                                    null,null,null,null,
                                                    color,
                                                    null);
    // thanks to David Huynh for pointing this out
    evt.marker = marker;
    events.push(evt);
    return(marker);
}

function zIndexByAge (marker, b) {
    // order markers by age, youngest on top
    return(Math.floor(marker.age * -100));
}

// this is directly from the Timeline tutorial
function onResize() {
    if (isIE) {
        document.getElementById('timeline').style.height = document.documentElement.clientHeight / 4;
    }
    if (resizeTimerID == null) {
        resizeTimerID = window.setTimeout(function() {
            resizeTimerID = null;
            timeline.layout();
        }, 500);
    }
}
